package com.editso.haier.arg;

import jdk.nashorn.internal.runtime.regexp.joni.exception.SyntaxException;

import java.util.*;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * 简单格式参数解析
 * 语法:
 * key = 标识符
 * arg = key(arg,arg)
 * args = <arg>:<arg>
 */
public class ArgParser {
    private final String origin;
    private final Map<String, List<Object>> args;
    private int index;

    public ArgParser(String origin) {
        this.origin = origin;
        this.args = new HashMap<>();
        this.parseKey();
    }

    private <T> String toArrayString(String sp, T[] o) {
        StringBuilder builder = new StringBuilder();
        for (T t : o) {
            builder.append(t).append(sp);
        }
        return builder.substring(0, builder.length() - sp.length());
    }

    private void accept(Character... c) {
        List<Character> characters = Stream.of(c).collect(Collectors.toList());
        while (true){
            if (!hasArg()) break;
            if (origin.charAt(index) == ' '){
                index++;
            }else if (characters.contains(origin.charAt(index))){
                index++;
                return;
            }else {
                break;
            }
        }
        throw new SyntaxException(
                "预期: " +
                toArrayString("|", c.clone()) +
                " 但是出现了: " + origin.charAt(index));
    }

    /**
     * 解析参数
     * */
    private List<Object> parseArg() {
        accept('(');
        List<Object> arg = new ArrayList<>();
        StringBuilder buff = new StringBuilder();
        char chr;
        while (true) {
            chr = origin.charAt(index);
            if (chr == ',' || chr == ')' || chr == ' ') {
                if (buff.length() > 0 && chr == ' ') accept(',', ')');
                if (buff.length() > 0) {
                    arg.add(buff.toString());
                    buff = new StringBuilder();
                }
            } else {
                buff.append(chr);
            }
            if (chr == ')')
                break;
            index++;
        }
        accept(')');
        return arg;
    }

    /**
     * 解析关键字
     * */
    private String readLetter() {
        char chr;
        StringBuilder builder = new StringBuilder();
        while (true) {
            if (!hasArg()) throw new SyntaxException("语法有误");
            chr = origin.charAt(index);
            if (chr == '('){
                return builder.toString();
            }else if (chr == ':') {
                throw new SyntaxException("语法错误");
            }else if (chr == ' ' && builder.length() > 0){
                accept('(');
                index--;
                continue;
            }else if (chr != ' '){
                builder.append(chr);
            }
            index++;
        }
    }

    private boolean hasArg() {
        return index < origin.length();
    }

    private void parseKey() {
        while (index < origin.length()) {
            this.args.put(readLetter(), parseArg());
            if (hasArg()) accept(':');
        }
    }

    public <T> Optional<List<T>> get(String key){
        List<T> objects = (List<T>) args.get(key);
        return objects == null? Optional.empty() : Optional.of(objects);
    }

    public <T> List<T> orGetEmpty(String key){
        return (List<T>) get(key).orElse(Collections.emptyList());
    }

    public <T> Stream<T> stream(String key){
        return (Stream<T>) orGetEmpty(key).stream();
    }

    public <T, R> R[] mapArray(String key, Function<T, R> map){
        return (R[]) ((Stream<T>)stream(key)).map(map).toArray();
    }

    public<T, R> void hasMap(String key, Function<T,R> map, Consumer<List<R>> consumer){
        List<T> objects = orGetEmpty(key);
        List<R> collect = objects.stream().map(map).collect(Collectors.toList());
        if (!collect.isEmpty()) consumer.accept(collect);
    }

    public <T, R> void hasMap(String key, BiFunction<T, Args, R> map, Consumer<List<R>> consumer){
        List<T> objects = orGetEmpty(key);
        int[] index = new int[1];
        Args args = new Args() {
            @Override
            public int index() {
                return index[0];
            }

            @Override
            public int size() {
                return objects.size();
            }
        };
        List<R> data = new ArrayList<>();
        R r = null;
        for (T object : objects){
            r = map.apply(object, args);
            if (r != null) data.add(r);
            index[0]++;
        }
        if (!data.isEmpty())
            consumer.accept(data);
    }
    
    public Optional<Object> get(String key, int index){
        List<Object> objects = get(key).orElse(Collections.emptyList());
        return index < objects.size() ? Optional.of(objects.get(index)) : Optional.empty();
    }

    public Optional<Integer> getInt(String key, int index){
        Optional<Object> o = get(key, index);
        Object o1 = o.orElse(null);
        return o1 == null ? Optional.empty() : Optional.of(Integer.valueOf(o1.toString()));
    }

    @Override
    public String toString() {
        return "ArgParser={" +
                "\n\torigin: " + origin +
                "\n\tparse: " + args.toString() +
                "\n}";
    }

    public static ArgParser parse(String s) {
        return new ArgParser(Objects.requireNonNull(s));
    }

    public static interface Args{
        int index();
        int size();
    }
}
